from vtk import vtkRenderer, vtkPolyDataMapper, vtkSphereSource, vtkPolyDataReader, vtkActor
from time import sleep, clock
from numpy import matrix
import wx
import os

##  PlaybackState provides the functionality for the Recording panel.
#   Plays the recorded movements.
class PlaybackState:

    ##  The constructor of PlaybackState.
    #   @param[in]  parent  The wx window (VisualizationState) of this panel.
    def __init__(self, parent, main):
        ##  The wx window (VisualizationState) of this panel.
        self.parent = parent
        ##  Main wx.App derived instance.
        self.main = main
        ##  Flag indicating whether the playback is on or off.
        self.PLAYING = False
        ##  Flag indicating whether this is the first session or not.
        self.FIRST_TIME = True
        ##  The list of recordings in the recordingslist.
        self.loadedRecordings = []
        ##  Variable for the current selected recording.
        self.selectedRecording = None
        # Initiate VTK render window for this state.
        self.parent.InitiateVTK(self)

    ##  Toggles the playback on or off.
    def OnTogglePlayPause(self):
        if self.PLAYING:
            self.PLAYING = False
        elif self.selectedRecording != None:
            self.PLAYING = True
            self.Play()

    ##  Plays recording when the PLAYING flag is On, until the recording has reached its end.
    def Play(self):
        while self.PLAYING:
            if self.selectedRecording.currentFrame < self.selectedRecording.numberOfFrames:

                startTime = clock()

                self.parent.update_actor_positions()
                self.selectedRecording.currentFrame += 1
                self.SetSlider(self.selectedRecording.currentFrame)

                endTime = clock()

                self.parent.main.Yield()

                if (endTime - startTime) < (1.0/self.parent.frameRate):
                    sleep((1.0/self.parent.frameRate) - (endTime - startTime))
            else:
                self.panel.OnTogglePlayPause(None)
        
                
    def GetRowVectors(self, matrix):
        row_vectors = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]        
        for row in range(0, 4):
            for col in range(0, 4):
                row_vectors[row][col] = matrix.GetElement(row, col)                
        return row_vectors
    
    ##  Stores the matrices of the humerus and scapula
    def PlayAndStore(self):
        self.selectedRecording.currentFrame = 0
        scapula_list = []
        humerus_list = []
        while self.selectedRecording.currentFrame < self.selectedRecording.numberOfFrames:
            startTime = clock()
            self.parent.update_actor_positions()
            
            matrices = self.parent.get_actor_matrices()            
            scapula_list.append( self.GetRowVectors(matrices[0]))
            humerus_list.append( self.GetRowVectors(matrices[1]))
            
            self.selectedRecording.currentFrame += 1
            self.SetSlider(self.selectedRecording.currentFrame)
            endTime = clock()
        self.WriteTransformsToFile(scapula_list)
        self.WriteTransformsToFile(humerus_list)
            
    def WriteTransformsToFile(self, matrix_list):
        dlg = wx.FileDialog(self.panel, message="Save Matrix File", defaultDir=os.getcwd(), defaultFile="",
                        wildcard="Txt-file (*.txt)|*.txt", style=wx.SAVE | wx.CHANGE_DIR | wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            txtpath = dlg.GetPath()  
            fileWriter = open(txtpath, 'w')                
        blRange = len(matrix_list)
        for i in range(0, blRange):
            for j in range(0,4):                        
                fileWriter.write('%.3f %.3f %.3f %.3f\n' % (matrix_list[i][j][0], matrix_list[i][j][1], matrix_list[i][j][2], matrix_list[i][j][3]))
            fileWriter.write('\n')
        fileWriter.close()
 

    ##  Event handler, stops playback of the recording.
    def OnStop(self, event):
        if self.PLAYING:
            self.panel.OnTogglePlayPause(None)
        elif self.selectedRecording != None:
            self.SetSlider(0)
            self.selectedRecording.currentFrame = 0
        self.parent.update_actor_positions()

    ##  Event handler, goes back one frame.
    def OnBackward(self, event):
        if self.selectedRecording != None:
            index = self.panel.recordingList.GetSelection()
            self.selectedRecording.currentFrame = max(self.selectedRecording.currentFrame-1, 0)
            self.parent.update_actor_positions()
            self.SetSlider(self.selectedRecording.currentFrame)

    ##  Event handler, goes one frame forward.
    def OnForward(self, event):
        if self.selectedRecording != None:
            index = self.panel.recordingList.GetSelection()
            self.selectedRecording.currentFrame = min(self.selectedRecording.currentFrame + 1, self.selectedRecording.numberOfFrames - 1)
            self.parent.update_actor_positions()
            self.SetSlider(self.selectedRecording.currentFrame)

    ##  Event handler, Sets the framenumber of the selected recording to the value of the scrollbar.
    def OnSlide(self, event):
        if self.selectedRecording != None:
            self.selectedRecording.currentFrame = self.panel.slider.GetValue()
            self.parent.update_actor_positions()

    ##  Event handler, deletes the selected recording.
    def OnDelete(self, event):
        if self.selectedRecording != None:
            index = self.panel.recordingList.GetSelection()
            msg = "Are you sure you want to delete '%s'?" % self.panel.recordingList.GetString(index)
            dlg = wx.MessageDialog(self.panel, msg, 'Warning', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_EXCLAMATION)
            if(dlg.ShowModal() == wx.ID_YES):
                if self.PLAYING:
                    self.panel.OnTogglePlayPause(None)
                try:
                    os.remove(self.selectedRecording.filename)
                    self.loadedRecordings.__delitem__(index)
                    self.panel.recordingList.Delete(index)
                except OSError:
                    msg = "Could not delete file '%s'" %self.selectedRecording.filename
                    dlgError = wx.MessageDialog(self.panel, msg, 'Error', wx.OK | wx.ICON_ERROR)
                    dlgError.ShowModal()
                    dlgError.Destroy()
                # If the list is empty, the selectedRecording will be set back to None value.
                if len(self.loadedRecordings) == 0:
                   self.selectedRecording = None
                   self.SetSlider(0)
                   self.panel.DisableFunctions()
                else:
                    self.panel.recordingList.Select(0)
                    self.OnItemSelected(None)
            dlg.Destroy()

    ##  Event handler for clicking the rename button.
    def OnRename(self, event):
        if self.selectedRecording != None:
            msg = "Enter a file name"
            dlg = wx.TextEntryDialog(self.panel,msg,'Rename',self.selectedRecording.filename)

            if dlg.ShowModal() == wx.ID_OK:
                index = self.panel.recordingList.GetSelection()
                string =  dlg.GetValue()
                if self.panel.recordingList.GetStrings().__contains__(string):
                    msg = "'%s' already exists" % string
                    dlgError = wx.MessageDialog(self.panel, msg, 'Error', wx.OK | wx.ICON_ERROR)
                    dlgError.ShowModal()
                    dlgError.Destroy()
                else:
                     try:
                         os.rename(self.selectedRecording.filename, string)
                         self.selectedRecording.filename = string
                         string = string.split("\\")
                         self.panel.recordingList.SetString(index, string[len(string)-1])
                     except OSError:
                        msg = "Could not rename file '%s'" % string
                        dlgError = wx.MessageDialog(self.panel, msg, 'Error', wx.OK | wx.ICON_ERROR)
                        dlgError.ShowModal()
                        dlgError.Destroy()
            dlg.Destroy()

    ##  Event handler for clicking the import button.
    #   Shows open file dialog with given message and wildcards on the given dir.
    def OnImport(self, event):
        # store the working directory
        defaultWorkingDir = os.getcwd()
        msg = "Select a recording to open"
        wildcard = "FOB file (*.fob)|*.fob|" \
               "All files (*.*)|*.*"
        dlg = wx.FileDialog(self.panel, msg, os.getcwd(), "", wildcard, wx.FD_OPEN|wx.FD_CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            try:
                path = dlg.GetPath()
                importedRecording = self.parent.recordingsProcessor.LoadRecording(path)
                importedRecording.filename = dlg.GetPath()
                self.addRecording(importedRecording)
            except IOError:
                msg = "Could not import file"
                dlgError = wx.MessageDialog(self.panel, msg, 'Error', wx.OK | wx.ICON_ERROR)
                dlgError.ShowModal()
                dlgError.Destroy()
        # Restore the working directory.
        os.chdir(defaultWorkingDir)


    ##  Event handler for the changing of selection in the recordings list.
    def OnItemSelected(self, event):
        newlySelectedRecording = self.loadedRecordings[self.panel.recordingList.GetSelection()]
        self.main.bonyLandmarks = newlySelectedRecording.bonyLandmarksRecorded

        if self.selectedRecording != newlySelectedRecording:
            self.selectedRecording = newlySelectedRecording
            self.panel.slider.SetMax(self.selectedRecording.numberOfFrames - 1)
            self.selectedRecording.currentFrame = 0
            self.panel.slider.SetValue(self.selectedRecording.currentFrame)
            self.panel.EnableFunctions()
            self.parent.SetRenderActors(self, len(self.selectedRecording.bonyLandmarksRecorded), len(self.selectedRecording.sensorsRecorded), len(self.selectedRecording.GHJointsRecorded))

            if self.PLAYING:
                self.panel.OnTogglePlayPause(None)
            self.parent.update_actor_positions()


    ##  Sets the frame number of the selected recording on the value of the slider.
    #   @param[in]  frameNumber The current frame number of the selected recording.
    def SetSlider(self, frameNumber):
        self.panel.slider.SetValue(frameNumber)
          
    ##  Adds a new recording to the list.
    #   @param[in]  newRecording    The new recording to add to the list.
    def addRecording(self, newRecording):
        self.loadedRecordings.append(newRecording)
        filename = newRecording.filename.split("\\")
        self.panel.recordingList.Append(filename[len(filename)-1])